package com.shadow.study.ip.client.utils;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Http请求调用工具类.
 */
public final class HttpUtils {

    /** 日志输出. **/
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);

    /** USER_AGENT. **/
    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";

    private HttpUtils() {
    }

    /** 超时设置. **/
    private static final RequestConfig requestConfig = RequestConfig.custom()
        .setConnectTimeout(5000).setConnectionRequestTimeout(5000).setSocketTimeout(10000).build();

    /** 附件下载超时设置. **/
    private static final String FILE_CONFIG = "requestFileConfig";
    private static final RequestConfig requestFileConfig = RequestConfig.custom()
        .setConnectTimeout(60000).setConnectionRequestTimeout(60000).setSocketTimeout(120000).build();

    /** 编码设置. **/
    private static final ConnectionConfig connectionConfig = ConnectionConfig.custom()
        .setMalformedInputAction(CodingErrorAction.IGNORE).setUnmappableInputAction(CodingErrorAction.IGNORE)
        .setCharset(Consts.UTF_8).build();

    /**
     * 构建HttpClient.
     * @param headersMap 头部参数map
     **/
    private static HttpClientBuilder getBuilder(final Map<String, String> headersMap) {
        final List<Header> headers = new ArrayList<>();
        final Header header = new BasicHeader("User-Agent", USER_AGENT);
        headers.add(header);
        //添加调用指定的Headers
        Optional.ofNullable(headersMap).filter(MapUtils::isNotEmpty).ifPresent(map ->
            map.entrySet().forEach(entry -> {
                Header headerMap = new BasicHeader(entry.getKey(),  entry.getValue());
                headers.add(headerMap);
            })
        );
        if (null != headersMap && FILE_CONFIG.equals(headersMap.get(FILE_CONFIG))) {
            return HttpClients.custom().setDefaultConnectionConfig(connectionConfig).setDefaultHeaders(headers)
                .setDefaultRequestConfig(requestFileConfig);
        }
        return HttpClients.custom().setDefaultConnectionConfig(connectionConfig).setDefaultHeaders(headers)
            .setDefaultRequestConfig(requestConfig);
    }

    /**
     * 发送无参数的HttpGet请求.
     * @param url 请求地址
     * @param headersMap 头部参数
     * @return java.lang.String
     **/
    public static ResultInfo sendGet(final String url, final Map<String, String> headersMap) {
        final ResultInfo result = new ResultInfo();
        final HttpGet httpGet = new HttpGet(url);
        try {
            final CloseableHttpClient httpclient = getBuilder(headersMap).build();
            final CloseableHttpResponse response = httpclient.execute(httpGet);
            final HttpEntity httpEntity = response.getEntity();
            result.setData(EntityUtils.toString(httpEntity));
            result.setResult(true);
        } catch (Exception e) {
            LOGGER.error("error occurred while executing the method(sendGet), url: [{}]", url, e);
            result.setInfo(e.getLocalizedMessage());
        }
        return result;
    }

    /**
     * 发送HttpPost请求，参数为map.
     * @param url url请求地址
     * @param headersMap headersMap请求头部
     * @param paramMap paramMap请求参数键值串
     **/
    public static ResultInfo sendPostParam(final String url, final Map<String, String> headersMap,
           final Map<String, Object> paramMap) {
        final ResultInfo result = new ResultInfo();
        try {
            //构建参数
            final List<NameValuePair> formParams = new ArrayList<>();
            Optional.ofNullable(paramMap).filter(MapUtils::isNotEmpty).ifPresent(map ->
                map.entrySet().stream().forEach(
                    entry -> formParams.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))))
            );
            final UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
            final HttpPost httppost = new HttpPost(url);
            httppost.setEntity(entity);
            CloseableHttpResponse response = null;
            //client调用
            final CloseableHttpClient httpclient = getBuilder(headersMap).build();
            response = httpclient.execute(httppost);
            final HttpEntity httpEntity = response.getEntity();
            result.setData(EntityUtils.toString(httpEntity));
            result.setResult(true);
        } catch (IOException e) {
            LOGGER.error(Marker.ANY_MARKER, ">>>>>>>>>>>>>httpclient sendPostParam [{0}] sendPostParam[{1}],error：[{2}]",
                url, paramMap, e);
            result.setInfo(e.getMessage());
        }
        return result;
    }

    /**
     * 发送HttpPost请求，参数为map.
     * @param url url请求地址
     * @param headersMap headersMap请求头部
     **/
    public static ResultInfo sendPostParamFile(final String url, final Map<String, String> headersMap) {
        final ResultInfo result = new ResultInfo();
        try {
            headersMap.put(FILE_CONFIG, FILE_CONFIG);
            final CloseableHttpClient httpclient = getBuilder(headersMap).build();
            final HttpGet get = new HttpGet(url);
            final HttpResponse response = httpclient.execute(get);
            if (200 == response.getStatusLine().getStatusCode()) {
                //得到实体
                final HttpEntity entity = response.getEntity();
                final byte[] data = EntityUtils.toByteArray(entity);
                final String msg = new String(data, StandardCharsets.ISO_8859_1.name());
                if ("auth failure!".equals(msg)) {
                    LOGGER.info("文件获取失败！auth failure!");
                } else if ("".equals(msg)) {
                    LOGGER.info("文件获取失败！文件为空");
                } else {
                    result.setData(msg);
                    result.setResult(true);
                    LOGGER.info("文件获取成功！");
                }
            }
        } catch (IOException e) {
            LOGGER.error("httpclient sendPostParamFile [{}]", url, e);
            result.setInfo(e.getMessage());
        }
        return result;
    }

    /**
     * 发送HttpPost请求，参数为json字符串.
     * @param url 请求地址
     * @param headersMap headersMap请求头部
     * @param jsonStr json参数串
     **/
    public static ResultInfo sendPostJson(final String url, final Map<String, String> headersMap, final String jsonStr) {
        final ResultInfo result = new ResultInfo();
        try {
            // 设置entity
            final StringEntity stringEntity = new StringEntity(jsonStr, StandardCharsets.UTF_8.name());
            stringEntity.setContentType("application/json");

            final HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(stringEntity);

            final CloseableHttpClient httpclient = getBuilder(headersMap).build();
            final CloseableHttpResponse httpResponse = httpclient.execute(httpPost);
            final HttpEntity httpEntity = httpResponse.getEntity();
            result.setData(EntityUtils.toString(httpEntity));
            result.setResult(true);
        } catch (Exception e) {
            LOGGER.error("httpclient sendPostJson [{}] sendPostParam[{}]", url, jsonStr, e);
            result.setInfo(e.getMessage());
        }
        return result;
    }

    /**
     * 发送HttpPost请求，提交表单，支持文件上传.
     * @param url 请求地址
     * @param params 表单参数
     * @param files 上传文件
     **/
    public static ResultInfo sendPostForm(final String url, final Map<String, Object> params,
          final List<MultipartFile> files) {
        final ResultInfo result = new ResultInfo();
        try {
            // 设置entity
            final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(StandardCharsets.UTF_8);
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            if (CollectionUtils.isNotEmpty(files)) { // 文件表单参数
                for (MultipartFile file : files) {
                    final Path path = Paths.get(file.getOriginalFilename());
                    String contentType = Files.probeContentType(path);
                    if (StringUtils.isEmpty(contentType)) {
                        contentType = "application/octet-stream";
                    }
                    builder.addBinaryBody(file.getName(), file.getInputStream(), ContentType.create(contentType),
                        file.getOriginalFilename());
                }
            }
            if (MapUtils.isNotEmpty(params)) { // 普通表单参数
                params.forEach((k, v) -> {
                    StringBody stringBody = new StringBody(v.toString(), ContentType.create("text/plain",
                        StandardCharsets.UTF_8.name()));
                    builder.addPart(k, stringBody);
                });
            }

            final HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(builder.build());
            final CloseableHttpClient httpclient = getBuilder(null).build();
            final CloseableHttpResponse httpResponse = httpclient.execute(httpPost);
            final HttpEntity httpEntity = httpResponse.getEntity();
            result.setData(EntityUtils.toString(httpEntity));
            result.setResult(true);
        } catch (Exception e) {
            LOGGER.error("httpclient sendPostForm [{}] sendPostParam[{}]", url, params, e);
            result.setInfo(e.getMessage());
        }
        return result;
    }

    /**
     * 发送HttpPost请求，提交表单，支持文件上传.
     * @param url 请求地址
     * @param params 表单参数
     * @param files 上传文件
     **/
    public static ResultInfo sendPostFile(final String url, final Map<String, Object> params, final List<MultipartFile> files) {
        ResultInfo result = new ResultInfo();
        try {
            // 设置entity
            final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(StandardCharsets.UTF_8);
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            if (CollectionUtils.isNotEmpty(files)) { // 文件表单参数
                for (MultipartFile file : files) {
                    final Path path = Paths.get(file.getOriginalFilename());
                    String contentType = Files.probeContentType(path);
                    if (StringUtils.isEmpty(contentType)) {
                        contentType = "application/octet-stream";
                    }
                    builder.addBinaryBody("file", file.getInputStream(), ContentType.create(contentType), file.getName());
                }
            }
            if (MapUtils.isNotEmpty(params)) { // 普通表单参数
                params.forEach((k, v) -> {
                    StringBody stringBody = new StringBody(v.toString(), ContentType.create("text/plain",
                        StandardCharsets.UTF_8.name()));
                    builder.addPart(k, stringBody);
                });
            }

            final CloseableHttpClient httpclient = getBuilder(null).build();
            final HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(builder.build());
            final CloseableHttpResponse httpResponse = httpclient.execute(httpPost);
            final HttpEntity httpEntity = httpResponse.getEntity();
            result.setData(EntityUtils.toString(httpEntity));
            result.setResult(true);
        } catch (Exception e) {
            LOGGER.error(Marker.ANY_MARKER, ">>>>>>>>>>>>>httpclient [{0}] sendPostParam[{1}],error：[{2}]", url, params, e);
            result.setInfo(e.getMessage());
        }
        return result;
    }
}
